﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Net;

namespace fleXdoc.Api
{
    public static class IOHelper
    {
        public static bool TryParseAsUri(string uriPath, out Uri uri)
        {
            try
            {
                uri = new Uri(uriPath);
                return true;
            }
            catch (UriFormatException)
            {
                uri = null;
                return false;
            }
        }

        public static MemoryStream GetWebResponse(Uri uri)
        {
            try
            {
                WebRequest req = WebRequest.Create(uri);
                HttpWebRequest httpReq = req as HttpWebRequest;
                if (httpReq != null)
                {
                    httpReq.UseDefaultCredentials = true;
                }

                MemoryStream memStream = null;
                using (WebResponse resp = req.GetResponse())
                {
                    memStream = LoadIntoMemoryStream(resp.GetResponseStream());
                }

                return memStream;
            }
            catch (Exception ex)
            {
                // Always return IOException when errors occur
                throw new IOException("Failed to open uri (" + uri.AbsolutePath + "): " + ex.Message, ex);
            }
        }

        public static MemoryStream GetFile(string path, FileMode mode, FileAccess access, FileShare share)
        {
            MemoryStream memStream = null;
            using (Stream fileStream = File.Open(path, mode, access, share))
            {
                memStream = LoadIntoMemoryStream(fileStream);
            }
            return memStream;
        }

        public static MemoryStream GetUri(string uriPath, FileMode mode, FileAccess access, FileShare share)
        {
            Uri uri = null;
            if (TryParseAsUri(uriPath, out uri))
            {
                return GetWebResponse(uri);
            }
            else
            {
                return GetFile(uriPath, mode, access, share);
            }
        }

        public static void CopyStream(Stream source, Stream target)
        {
            long srcPos = (source.CanSeek ? source.Position : long.MinValue);
            long destPos = (target.CanSeek ? target.Position : long.MinValue);

            const int bufSize = 0x1000;
            byte[] buf = new byte[bufSize];
            int bytesRead = 0;
            while ((bytesRead = source.Read(buf, 0, bufSize)) > 0)
            {
                target.Write(buf, 0, bytesRead);
            }

            MemoryStream targetAsMemStream = target as MemoryStream;
            if (targetAsMemStream != null)
            {
                // Don't use any more memory than needed
                targetAsMemStream.Capacity = Convert.ToInt32(targetAsMemStream.Length);
            }

            // Seek streams back to original position
            if (target.CanSeek)
            {
                target.Position = destPos;
            }
            if (source.CanSeek)
            {
                source.Position = srcPos;
            }
        }

        public static MemoryStream LoadIntoMemoryStream(Stream template)
        {
            if (template == null)
            {
                throw new ArgumentNullException("template");
            }
            if (!template.CanRead)
            {
                throw new ArgumentException("template cannot be read (write only?)", "template");
            }

            int initialCapacity = 32768; 
            try
            {
                long templateSize = template.Length;
                initialCapacity = Convert.ToInt32(templateSize);
            }
            catch (NotSupportedException) { }
            
            MemoryStream memStream = new MemoryStream(initialCapacity);
            IOHelper.CopyStream(template, memStream); // position of template is used as is, so caller must set it
            return memStream;
        }

    }
}
